#ifndef _CANARY2_NET_CORO_TCP_H_
#define _CANARY2_NET_CORO_TCP_H_

#include "tcp.h"
#include <concepts>
#include <type_traits>
#include <functional>
#include <coroutine>
#include "../coro/epoll_suspended_manager.h"
#include <system_error>
#include <cstddef>
#include <unistd.h> // ::open

namespace wilson::net
{
    extern ::std::error_code shutdown_for_writing;

    enum io_state : ssize_t { INIT = -2, ERR = -1 };

    struct tcp_io_base
    {
        tcp_io_base(
            coro::epoll_suspend_manager& esm, 
            tcp_io& io, 
            int flags, 
            ::std::error_code* e = nullptr
        ) : ia_epoll_susp_mgr{ esm }, 
            ia_io{ io }, 
            ia_flags{ flags }, 
            ia_ec{ e }
        {
        }

        coro::epoll_suspend_manager& ia_epoll_susp_mgr;
        tcp_io& ia_io;
        ssize_t ia_result{ ssize_t(INIT) };
        int     ia_flags;
        ::std::error_code* ia_ec{};
    };

    class send_file_aw : private tcp_io_base
    {
    public:
        send_file_aw(
            coro::epoll_suspend_manager& esm, 
            tcp_io& io, 
            const ::std::filesystem::path& file_path,
            int flags, 
            ::std::error_code* e = nullptr
        ) : tcp_io_base{ 
                esm, io, flags, e 
            } 
        {
            auto path_str = file_path.string();
            m_fd = sys::fd_guard{ ::open(path_str.c_str(), O_RDONLY) };
            m_file_size = file_size(file_path);
        }

        bool await_ready() noexcept;
        void await_suspend(::std::coroutine_handle<> h);
        size_t await_resume();

    private:
        sys::fd_guard m_fd;
        ::std::uintmax_t m_file_size;
    };
    
    class send_aw : private tcp_io_base
    {
    public:
        send_aw(
            coro::epoll_suspend_manager& esm,
            tcp_io& io, 
            ::std::span<const ::std::byte> buffer, 
            int flags, 
            ::std::error_code* ec = nullptr
        );

        send_aw(
            coro::epoll_suspend_manager& esm,
            tcp_io& io, 
            ::std::string_view buffer, 
            int flags, 
            ::std::error_code* ec = nullptr
        ) : send_aw(
            esm, 
            io, 
            ::std::span{ 
                reinterpret_cast<const ::std::byte*>(buffer.data()), 
                buffer.size() 
            }, 
            flags, ec)
        {
        }

        bool await_ready() noexcept;
        void await_suspend(::std::coroutine_handle<> h);
        size_t await_resume();

    private:
        ::std::span<const ::std::byte> ia_buffer;
    };

    class recv_aw : private tcp_io_base
    {
    public:
        recv_aw(
            coro::epoll_suspend_manager& esm,
            tcp_io& io, 
            ::std::span<::std::byte> buffer, 
            int flags,
            ::std::error_code* ec = nullptr
        );

        bool await_ready() noexcept;
        void await_suspend(::std::coroutine_handle<> h);
        size_t await_resume();

    private:
        ::std::span<::std::byte> ia_buffer;
    };

    class sendmsg_aw : private tcp_io_base
    {
    public:
        sendmsg_aw(
            coro::epoll_suspend_manager& esm,
            tcp_io& io, 
            const msghdr* msg, 
            int flags,
            ::std::error_code* ec = nullptr
        );
        
        bool await_ready() noexcept;
        void await_suspend(::std::coroutine_handle<> h);
        size_t await_resume();

    private:
        const msghdr* ia_buffer;
    };

    class recvmsg_aw : private tcp_io_base
    {
    public:
        recvmsg_aw(
            coro::epoll_suspend_manager& esm,
            tcp_io& io, 
            msghdr* msg, 
            int flags, 
            ::std::error_code* ec = nullptr
        );

        bool await_ready() noexcept;
        void await_suspend(::std::coroutine_handle<> h);
        size_t await_resume();

    private:
        msghdr* ia_buffer;
    };
}

#endif
